//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <vkey.h>
#include <playsound.h>

Boolean _IsAudioBusy();
void _AudioLock();
void _AudioUnlock();
EXTERN_C void * Drv_AllocBuffer(unsigned int uPages, unsigned int uFlag);
EXTERN_C void Drv_FreeBuffer(void * VirtAddr, unsigned int uPages);

#define DBGKeySound(l,s)    //s

static CPlaySound g_PlaySound;

ECode CPlaySound::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    if (pBuffer == NULL || !pBuffer->GetCapacity()) {
        return E_INVALID_ARGUMENT;
    }

    return NOERROR;
}

ECode  CPlaySound::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ Int32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode PlayMidiThread(void *arg)
{
    CPlaySound * pThis=(CPlaySound *)arg;
    while (1) {
        DBGKeySound(0, kprintf("====================Wait for play ...\n"));
        MemoryBuf_<4> ebbData;
        ECode ec = pThis->m_pQueues->Read(EVENT_KEYSOUND, 4, &ebbData, NULL);
        if (FAILED(ec)) {
            DBGKeySound(0, kprintf("No Data\n"));
            continue;
        }
        int tracknum = *(int *)ebbData.GetPayload();
        if (tracknum < 0) break;//Exit
        if (tracknum > _MAX_KEY) continue;

        pThis->m_tracknum = tracknum;
        DBGKeySound(0, kprintf("Play sound %d\n", tracknum));
        pThis->PlaySound();
    }

    pThis->m_State = STOP;
    DBGKeySound(0, kprintf("Play Midi Thread Exit\n"));

    return NOERROR;
}

ECode CPlaySound::PlaySound()
{
    ECode ec;
    MemoryBuf_<16> buf;
    Int64 cmd;
    UInt16 vol;
    char *pBuffer;
    int BufSize;

    if (m_pAudio == NULL) {
        ec = DzFindService(L"device:wave0", (IInterface **)&m_pAudio);
        assert(SUCCEEDED(ec));
    }

    _AudioLock();

    if (_IsAudioBusy()) {
        DBGKeySound(0, kprintf("Audio Busy\n"));
        ec = E_ACCESS_DENIED;
        goto Exit;
    }

    pBuffer = (char *)m_RawArr[m_tracknum].buf;
    BufSize = m_RawArr[m_tracknum].siz;
    if (BufSize == 0) {
        DBGKeySound(0, kprintf("No raw"));
        ec = E_ACCESS_DENIED;
        goto Exit;
    }

#define RATE          44100
#define BITS          16
#define CHANNELS      1

    cmd = (Int64)(RATE & 0xFFFFFFFF)
                       | ((Int64)(BITS & 0xFFFF)<< 32)
                       | ((Int64)(CHANNELS & 0xFF) << 48);
    buf.Copy((Byte *)&cmd, sizeof(cmd));
    ec = m_pAudio->Control(0x00, buf, &buf, NULL);
    assert(SUCCEEDED(ec));

#define VOLUME       30 //default

    vol = m_volume;
    buf.Copy((Byte *)&vol, sizeof(vol));
    ec = m_pAudio->Control(1/* Master Volume */, buf, &buf, NULL);
    assert(SUCCEEDED(ec));

    DBGKeySound(0, kprintf("Open audio ok\n"));

    m_State = BUSY;

    while (BufSize > 0) {
        Int32 bytes_write;
        MemoryBuf ebbData((Byte *)pBuffer, BufSize, BufSize);
        m_pAudio->Write(0, ebbData, &bytes_write, NULL);
        DBGKeySound(0, kprintf("bytes_write = %d\n", bytes_write));
        BufSize-=bytes_write;
        pBuffer+=bytes_write;
    }

    m_State = IDLE;

    //Flush out
    ec = m_pAudio->Control(0x10, NULL_MEMORYBUF, NULL, NULL);

Exit:
    _AudioUnlock();
    return ec;
}

ECode CPlaySound::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    DBGKeySound(1, kprintf("Playsound::Control %x\n", nControlCode));
    int nFuncCode = nControlCode & 0xffff;

    switch (nFuncCode) {
        case CTRL_OPEN_KEYSOUND:{
            int e = EVENT_KEYSOUND;
            MemoryBuf box((Byte *)&e, 16, 16);
            m_pQueues->Control(EVENT_KEYSOUND << 16 | 5, box, NULL, NULL);
            break;
        }
        case CTRL_OPEN_TOUCHSOUND:{
            int e = EVENT_TOUCHSOUND;
            MemoryBuf box((Byte *)&e, 16, 16);
            m_pQueues->Control(EVENT_KEYSOUND << 16 | 5, box, NULL, NULL);
            break;
        }
        case CTRL_CLOSE_KEYSOUND:{
            m_pQueues->Control(EVENT_KEYSOUND << 16 | 2,
                NULL_MEMORYBUF, NULL, NULL);
            break;
        }
        case CTRL_CLOSE_TOUCHSOUND:{
            m_pQueues->Control(EVENT_TOUCHSOUND << 16 | 2,
                NULL_MEMORYBUF, NULL, NULL);
            break;
        }

        case CTRL_SET_PLAYSOUND:{
            int i = nControlCode >> 16;//Keycode
            if (i < 0 || i > _MAX_KEY) return E_ACCESS_DENIED;
            int len = inBuffer.GetUsed();
            //s /= 2;//reduce

            if (m_RawArr[i].siz < len) {//realloc
                if (m_RawArr[i].buf) Drv_FreeBuffer(m_RawArr[i].buf, 0);
                m_RawArr[i].buf = Drv_AllocBuffer((len+0xfff)>>12, 0);
            }
            memcpy(m_RawArr[i].buf, inBuffer.GetPayload(), len);
            m_RawArr[i].siz = len;
            DBGKeySound(1, kprintf("================Playsound %d, %x(%d)\n\n",
                i, m_RawArr[i].buf, m_RawArr[i].siz));
            break;
        }
        case CTRL_SET_PLAYVOLUME:{
            if (inBuffer.GetUsed() >= 2) return E_ACCESS_DENIED;
            m_volume = *(UInt16 *)inBuffer.GetPayload();
            DBGKeySound(1, kprintf("================Playsound volume %d\n",
                m_volume));
            break;
        }
        default:
            break;
    }

    return NOERROR;
}

void CPlaySound::Dispose()
{
    if (m_pAudio) {
        m_pAudio->Release();
    }
}

ECode CPlaySound::Initialize()
{
    m_volume = VOLUME;
    m_State = STOP;
    m_tracknum = 12;
    m_pAudio = NULL;
    m_pQueues = NULL;
    memset(m_RawArr, 0, sizeof(m_RawArr));
    m_RawArr[_MAX_KEY].buf = Drv_AllocBuffer(1, 0);
    memset(m_RawArr[_MAX_KEY].buf, 0, 4096);
    m_RawArr[_MAX_KEY].siz = 4096;

    ECode ec;
    if (m_State == STOP) {
        if (m_pQueues == NULL) {
            if (!m_pQueues) {
                ec = DzFindService(L"device:eq0", (IInterface **)&m_pQueues);
                assert(SUCCEEDED(ec));
            }
        }
        QueueInfo Info = {EVENT_KEYSOUND, 4, 3};
        MemoryBuf box((Byte *)&Info, 16, 16);
        m_pQueues->Control(1, box, NULL, NULL);//Create New queue

        ec = DzCreateKernelThread(PlayMidiThread,
            (PVoid)this, 0x80, ThreadPriorityRank_Normal, NULL);
        if (FAILED(ec)) {
            DBGKeySound(1, kprintf("CreateThread error\n"));
            return ec;
        }
        DBGKeySound(0, kprintf("Create play thread ok\n"));
        m_State = IDLE;
    }

    return NOERROR;
}

void AudioWake()
{
    CPlaySound * pThis = &g_PlaySound;
    pThis->m_tracknum = 15/*_MAX_KEY*/;//Clean;
    DBGKeySound(0, kprintf("Play sound %d\n", pThis->m_tracknum));
    pThis->PlaySound();
}

EXTERN IDeviceDriver * CDECL CreatePlaysound(uint_t uDeviceNo, void *pvParameter)
{
    CPlaySound * pThis = &g_PlaySound;
    pThis->Initialize();

    DBGKeySound(0, kprintf("Add PlaySound %08x OK!\n", pThis));
    return pThis;
}
